package sql_table

import (
	"errors"
	"fmt"
	"go-kvsql/db/mysqldb"
	"go-kvsql/sql_table/sql_builder"
	"go-kvsql/sync_log_table"
	"go-kvsql/sync_log_table/sync_log"
	"go-kvsql/task_calculator"
	"runtime"
	"strings"
)

type SqlTable struct {
	mysqlDb            *mysqldb.MysqlDb
	syncLogTable       *sync_log_table.SyncLogTable
	setSqlBuilder      *sql_builder.SqlBuilder
	lastTaskIndex      int64
	taskBufferArray    []chan sync_log.SyncLog
	arrayCountDesigner *task_calculator.TaskCalculator
}

func NewSqlTable(tableName string, mysqlDb *mysqldb.MysqlDb, syncLogTable *sync_log_table.SyncLogTable,
	taskBufferSize int) (*SqlTable, error) {

	tableName = strings.TrimSpace(tableName)
	if len(tableName) < 1 {
		return nil, errors.New("tableName is empty.")
	}
	if mysqlDb == nil {
		return nil, errors.New("mysqlDb is nil.")
	}
	if syncLogTable == nil {
		return nil, errors.New("syncLogTable is nil.")
	}

	arrayCountDesigner := task_calculator.NewTaskCalculator(runtime.NumCPU() * 2)
	sqlTable := &SqlTable{mysqlDb: mysqlDb, syncLogTable: syncLogTable,
		setSqlBuilder: sql_builder.NewSqlBuilder(tableName), arrayCountDesigner: arrayCountDesigner,
	}
	commitTaskCount := arrayCountDesigner.TaskCount
	sqlTable.lastTaskIndex = int64(commitTaskCount) - 1
	sqlTable.taskBufferArray = make([]chan sync_log.SyncLog, commitTaskCount)

	for i := 0; i < commitTaskCount; i++ {
		sqlTable.taskBufferArray[i] = make(chan sync_log.SyncLog, taskBufferSize)
		go sqlTable.commitTask(i)
	}
	return sqlTable, nil
}

func (this *SqlTable) SyncOperate(syncLog sync_log.SyncLog) (err error) {
	var sql string
	if syncLog.LogType == sync_log.Del {
		sql = this.setSqlBuilder.DelSql(syncLog.Key)
	} else {
		sql = this.setSqlBuilder.SetSql(syncLog.Value)
	}
	_, err = this.mysqlDb.Exec(sql)
	if err != nil {
		return err
	} else {
		return this.syncLogTable.Delete(syncLog.LogKeyBytes)
	}
}

func (this *SqlTable) AsyncOperate(syncLog sync_log.SyncLog) {
	taskIndex := this.arrayCountDesigner.ComputeIndex(syncLog.Key)
	this.taskBufferArray[taskIndex] <- syncLog
}

func (this *SqlTable) commitTask(index int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("commitTask-recover-error:", err)
			go this.commitTask(index)
		}
	}()

	for {
		syncLog := <-this.taskBufferArray[index]
		this.SyncOperate(syncLog)
	}
}
